/*
   Mathieu Stefani, 27 février 2016

   A special bind() method for REST routes
*/

#pragma once

namespace Pistache
{
    namespace Rest
    {
        namespace Route
        {

            void Get(Router& router, std::string resource, Route::Handler handler);
            void Post(Router& router, std::string resource, Route::Handler handler);
            void Put(Router& router, std::string resource, Route::Handler handler);
            void Delete(Router& router, std::string resource, Route::Handler handler);

            namespace details
            {
                template <class... Args>
                struct TypeList
                {
                    template <size_t N>
                    struct At
                    {
                        static_assert(N < sizeof...(Args), "Invalid index");
                        typedef typename std::tuple_element<N, std::tuple<Args...>>::type Type;
                    };
                };

                template <typename... Args>
                void static_checks()
                {
                    static_assert(sizeof...(Args) == 2, "Function should take 2 parameters");
                    typedef details::TypeList<Args...> Arguments;
                    // Disabled now as it
                    // 1/ does not compile
                    // 2/ might not be relevant
#if 0
            static_assert(std::is_same<Arguments::At<0>::Type, const Rest::Request&>::value, "First argument should be a const Rest::Request&");
            static_assert(std::is_same<typename Arguments::At<0>::Type, Http::Response>::value, "Second argument should be a Http::Response");
#endif
                }
            } // namespace details

            template <typename Result, typename Cls, typename... Args, typename Obj>
            Route::Handler bind(Result (Cls::*func)(Args...), Obj obj)
            {
                details::static_checks<Args...>();

                return [=](const Rest::Request& request, Http::ResponseWriter response) {
                    (obj->*func)(request, std::move(response));
                };
            }

            template <typename Result, typename Cls, typename... Args, typename Obj>
            Route::Handler bind(Result (Cls::*func)(Args...), std::shared_ptr<Obj> objPtr)
            {
                details::static_checks<Args...>();

                return [=](const Rest::Request& request, Http::ResponseWriter response) {
                    (objPtr.get()->*func)(request, std::move(response));
                };
            }

            template <typename Result, typename... Args>
            Route::Handler bind(Result (*func)(Args...))
            {
                details::static_checks<Args...>();

                return [=](const Rest::Request& request, Http::ResponseWriter response) {
                    func(request, std::move(response));
                };
            }

        } // namespace Route
    } // namespace Rest
} // namespace Pistache
